﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using TotalCrytography.Protector;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
namespace TotalCrytography.SystemFile
{
    public enum TypeOfFile
    {
        File,
        Folder
    }
    public class MyFile
    {
        String _fileName;

        public String FileName
        {
            get { return _fileName; }
            set { _fileName = value; }
        }
        List<byte> _text;

        public List<byte> Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public MyFile()
        {
            _fileName = String.Empty;
            _text = new List<byte>();
        }
        
        public MyFile(String pathFile)
        {
            //_fileName = pathFile.LastIndexOf('\\') > -1 ? getNameFileFromPath(pathFile) : pathFile;
            _fileName = getNameFileFromPath(pathFile);
            try
            {
                _text = File.ReadAllBytes(pathFile).ToList();
            }
            catch (System.Exception ex)
            {
            	
            }
            
        }

        //private String key;
        //private String iv;
        //public bool WriteBinary(String output)
        //{
        //    try
        //    {
        //        FileStream fs = new FileStream(output, FileMode.Append, FileAccess.Write);
        //        BinaryWriter bw = new BinaryWriter(fs);
        //        bw.Write(_fileName);
        //        bw.Write(_text.Length);
        //        bw.Write(_text);
        //        bw.Close();
        //        fs.Close();
        //        return true;
        //    }
        //    catch
        //    {
        //        return false;
        //    }
        //}
        //public bool ReadBinary(String input)
        //{
        //    try
        //    {
        //        FileStream fs = new FileStream(input, FileMode.Open, FileAccess.Read);
        //        BinaryReader br = new BinaryReader(fs);
        //        _fileName = br.ReadString();
        //        int length = br.ReadInt32();
        //        _text = br.ReadBytes(length);
        //        br.Close();
        //        fs.Close();
        //        return true;
        //    }
        //    catch
        //    {
        //        return false;
        //    }
        //}
        private String getNameFileFromPath(String path)
        {
            return path.Substring(path.LastIndexOf('\\')+1);
        }

        public byte[] ConvertToBytes()
        {
            List<byte> result = new List<byte>();
            result.Add(Convert.ToByte(_fileName.Length));
            result.AddRange( Encoding.Default.GetBytes(_fileName));
            result.AddRange(_text);

            return result.ToArray();

            //return _text.ToArray();
                   
        }
        public bool ParseFromBytes(byte[] input)
        {
            try
            {
                int lengthNameFile = Convert.ToInt32(input[0]);
                List<byte> nameFile = new List<byte>();
                nameFile = input.ToList().GetRange(1, lengthNameFile);
                _fileName = Encoding.Default.GetString(nameFile.ToArray());//sua?
                _text = input.ToList().GetRange(1 + lengthNameFile, input.Length - 1 - lengthNameFile);
                
                return true;
            }
            catch
            {
                return false;
            }
        }
        public void Encrypt(String input,String output,TypeOfFile fm, Symmetric_Provider sp, int al, int pm, int cm, String publicKey)
        {
            FileStream fs2 = new FileStream(input, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            BinaryReader br2 = new BinaryReader(fs2);
            
            _text = br2.ReadBytes((int)fs2.Length).ToList();
            //File.Open(input, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            //File.ReadAllBytes(input);
            br2.Close();
            fs2.Close();
            //File.Delete(input);
            //content of file
            String key = "06121150612101";//we can random here
            //Symmetric_Provider sp = new Symmetric_Provider(;
            sp.GenerateKeyAndIV();
            sp.Key = sp.GetLegalKey(key);
            //spinput.GenerateKeyAndIV();
            //sp = new Symmetric_Provider(key);
            
            
            
            byte[] text_enc = sp.EncryptBytes(ConvertToBytes());
            //byte[] text_enc = sp1.EncryptBytes(_text);
            //byte[] text_enc = sp1.EncryptBytes(new byte[]{1});
            String iv = sp.GetIV();

            //use public key to encrypt key and iv
            String keyandiv =  key.Length.ToString() + "@" + key + iv.Length.ToString() + "@" + iv;
            String keyandiv_enc_str = RSA_Algorithm.encryptString(keyandiv, publicKey);
            

            //write to file
            FileStream fs1 = new FileStream(output, FileMode.Create, FileAccess.ReadWrite);
            BinaryWriter bw = new BinaryWriter(fs1);
            if (fm ==  TypeOfFile.File)
                bw.Write(0);
            else
                bw.Write(1);
            //write sp
            //bw.Write(algrorithm);
            //BinaryFormatter bformatter = new BinaryFormatter();
            //bformatter.Serialize(fs1, sp);
            bw.Write(al);
            bw.Write(pm);
            bw.Write(cm);

            bw.Write(keyandiv_enc_str.Length);
            bw.Write(keyandiv_enc_str);
            bw.Write(text_enc.Length);
            bw.Write(text_enc);
            bw.Close();
            fs1.Close();
            
        }

        //public bool Decrypt()
        //{
        //    return Decrypt(_fileName);
        //}

        //public bool DecryptFile()
        //{
        //    return DecryptFile(_fileName);
        //}
        
        //public bool DecryptFolder()
        //{
        //    return DecryptFolder(_fileName);
        //}
        public bool DecryptFolder(String output, FileStream fs)
        {
            return true;
        }
        public bool DecryptFile(String output, FileStream fs)
        {
            return true;
        }
        public bool Decrypt(String input, String output, String pass, String inputPrivateKey)
        {
            try
            {
                //read from file
                FileStream fs1 = new FileStream(input, FileMode.Open, FileAccess.Read);
                
                BinaryReader br = new BinaryReader(fs1);
                TypeOfFile fm;
                if (br.ReadInt32() == 0)
                    fm = TypeOfFile.File;
                else
                    fm = TypeOfFile.Folder;
                int al = br.ReadInt32();
                int pm = br.ReadInt32();
                int cm = br.ReadInt32();

                Symmetric_Provider sp2 = null;

                //read Symmetric_Privider
                //BinaryFormatter bformatter = new BinaryFormatter();
                //sp2 = (Symmetric_Provider)bformatter.Deserialize(fs1);


                int lengthkeyandiv = br.ReadInt32();
                String keyandiv_enc = br.ReadString();
                int lengthtext = br.ReadInt32();
                byte[] text_enc = br.ReadBytes(lengthtext);
                br.Close();
                fs1.Close();

                //use private key to decrypt key and iv
                //load privatekey
                sp2 = new Symmetric_Provider(pass);
                String privatekey = sp2.DecryptString(inputPrivateKey);
                String keyandiv = RSA_Algorithm.decryptString(keyandiv_enc, privatekey);
                
                //use key and iv to decrypt content of file
                // - get key and iv from keyiv
                String key,iv,keylengthstr,ivlengthstr;
                keylengthstr = string.Empty;
                ivlengthstr = string.Empty;
                key = string.Empty;
                iv = string.Empty;
                int count=0;
                while (keyandiv[count] != '@')
                {
                    keylengthstr += keyandiv[count];
                    count++;
                }
                count++;
                for (int i = 0; i < Convert.ToInt32(keylengthstr); i++)
                    key += keyandiv[count+i];
                count += Convert.ToInt32(keylengthstr);
                while (keyandiv[count] != '@')
                {
                    ivlengthstr += keyandiv[count];
                    count++;
                }
                count++;
                for (int i = 0; i < Convert.ToInt32(ivlengthstr); i++)
                    iv += keyandiv[count + i];
                //- use al, pm, cm, key and iv to decrypt file
                SymmetricAlgorithm saTemp = null;
                switch (al)
                {
                    case 0:
                        saTemp= new DESCryptoServiceProvider();
                        break;
                    case 1:
                        saTemp = new TripleDESCryptoServiceProvider();
                        break;
                    case 2:
                        saTemp = new RC2CryptoServiceProvider();
                        break;
                    case 3:
                        saTemp = new RijndaelManaged();
                        break;
                    default:
                        break;
                }
                PaddingMode pmTemp = PaddingMode.ANSIX923;
                switch (pm)
                {
                    case 0:
                        pmTemp = PaddingMode.ANSIX923;
                        break;
                    case 1:
                        pmTemp = PaddingMode.PKCS7;
                        break;
                    case 2:
                        pmTemp = PaddingMode.ISO10126;
                        break;
                }
                CipherMode cmTemp = CipherMode.ECB;
                switch (cm)
                {
                    case 0:
                        cmTemp = CipherMode.ECB;
                        break;
                    case 1:
                        cmTemp = CipherMode.CBC;
                        break;
                    case 2:
                        cmTemp = CipherMode.OFB;
                        break;
                    case 3:
                        cmTemp = CipherMode.CFB;
                        break;
                    case 4:
                        cmTemp = CipherMode.CTS;
                        break;
                }
                Symmetric_Provider sp = new Symmetric_Provider(key, iv, saTemp, pmTemp, cmTemp);
                
                ParseFromBytes(sp.DecryptBytes(text_enc));
                
                if (fm == TypeOfFile.Folder)
                {
                    File.WriteAllBytes(output + _fileName + ".temp2",_text.ToArray());
                    MyFile mf = new MyFile(output + _fileName + ".temp2");
                    mf.ExtractFolderFromTempFile(output + _fileName + ".temp2", output);
                    File.Delete(output + _fileName + ".temp2");
                    //File.Delete(output + _fileName);
                }
                else
                {
                    File.WriteAllBytes(output + _fileName, _text.ToArray());
                }
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        private bool ExtractFolderFromTempFile(String input,String output)
        {
            try
            {
                //make file from folder
                FileStream fs = new FileStream(input, FileMode.Open);
                BinaryReader br = new BinaryReader(fs);

                //first read folder name
                String folderName = br.ReadString();
                //create folder
                Directory.CreateDirectory(output +folderName);
                //read num of files
                int numoffiles = br.ReadInt32();
                //extract all file
                for (int i = 0; i < numoffiles; i++)
                {
                    TypeOfFile type;
                    if (br.ReadInt32() == 0)//is file
                        type = TypeOfFile.File;
                    else// is folder
                        type = TypeOfFile.Folder;
                    
                    int l = br.ReadInt32();
                    byte[] temp = br.ReadBytes(l);
                    MyFile tempFile = new MyFile();
                    tempFile.ParseFromBytes(temp);
                    FileStream fs1 = new FileStream(output +folderName+ '\\'+tempFile._fileName, FileMode.Create, FileAccess.Write);
                    fs1.Write(tempFile._text.ToArray(), 0, tempFile._text.Count);//sua? duong` dan? _fileName
                    fs1.Close();

                    if (type == TypeOfFile.Folder)
                    {
                        MyFile mf = new MyFile(output + folderName + '\\' + tempFile._fileName);
                        mf.ExtractFolderFromTempFile(output + folderName + '\\' + tempFile._fileName, output + folderName + '\\');
                        File.Delete(output + folderName + '\\' + tempFile._fileName);
                    }
                }

                
                br.Close();
                fs.Close();
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }



        
    }
}
